package com.houkunlin.wake.lan.scan;

import com.houkunlin.dao.PageVo;
import com.houkunlin.system.common.MessageWrapper;
import com.houkunlin.system.common.exception.BusinessException;
import com.houkunlin.wake.lan.host.HostInfo;
import com.houkunlin.wake.lan.host.HostInfoService;
import com.houkunlin.wake.lan.host.HostInfoTransform;
import io.swagger.annotations.Api;
import io.swagger.annotations.ApiOperation;
import org.springframework.http.HttpHeaders;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.*;

import java.time.Duration;
import java.time.LocalDateTime;
import java.util.Collections;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.Future;
import java.util.function.Function;
import java.util.stream.Collectors;

/**
 * Controller：局域网扫描
 *
 * @author HouKunLin
 */
@Api(tags = "局域网扫描")
@RestController
@RequestMapping("lan-scan")
public class ScanController {
    private final NmapService nmapService;
    private final HostInfoService hostInfoService;
    private final HostInfoTransform hostInfoTransform;
    private LocalDateTime startTime = LocalDateTime.MIN;
    private Future<PageVo<NmapVo>> future;
    private static final PageVo<NmapVo> PAGE_VO = new PageVo<>(Collections.emptyList(), 1L, 0, 0, true, true, true, Collections.emptyList());

    static {
        PAGE_VO.put("code", "01");
        PAGE_VO.put("msg", "系统正在扫描中，请继续等待");
    }

    public ScanController(final NmapService nmapService, final HostInfoService hostInfoService, final HostInfoTransform hostInfoTransform) {
        this.nmapService = nmapService;
        this.hostInfoService = hostInfoService;
        this.hostInfoTransform = hostInfoTransform;
    }

    /**
     * Index page vo.
     *
     * @return the page vo
     */
    @ApiOperation("获取局域网扫描结果列表")
    @GetMapping
    public PageVo<NmapVo> index() throws ExecutionException, InterruptedException {
        if (future == null) {
            throw new BusinessException("未触发局域网扫描，请主动触发扫描");
        }
        if (future.isDone()) {
            final PageVo<NmapVo> vo = future.get();
            if (vo == null) {
                throw new BusinessException("扫描失败，请重新扫描");
            }
            final List<NmapVo> nmapVos = vo.getList();
            final Set<String> macSets = nmapVos.stream().map(NmapVo::getMac).collect(Collectors.toSet());
            if (!macSets.isEmpty()) {
                final List<HostInfo> hostInfos = hostInfoService.lambdaQuery().in(HostInfo::getMac, macSets).list();

                final Map<String, HostInfo> hostInfoMap = hostInfos
                        .stream()
                        .collect(Collectors.toMap(HostInfo::getMac, Function.identity()));
                for (final NmapVo nmapVo : nmapVos) {
                    final HostInfo hostInfo = hostInfoMap.get(nmapVo.getMac());
                    nmapVo.setHostInfo(hostInfoTransform.toNmapVo(hostInfo));
                }
            }
            return vo;
        }
        PAGE_VO.put("startTime", startTime);
        PAGE_VO.put("duration", Duration.between(startTime, LocalDateTime.now()).toMillis());
        return PAGE_VO;
    }

    @ApiOperation("触发局域网扫描")
    @PostMapping
    public Object scan() {
        if (future == null) {
            startTime = LocalDateTime.now();
            future = nmapService.doScan("192.168.2.0/24");
        } else {
            if (future.isDone() || future.isCancelled()) {
                startTime = LocalDateTime.now();
                future = nmapService.doScan("192.168.2.0/24");
            } else {
                throw new BusinessException("系统正在扫描中，请等待扫描结果，请勿重复扫描");
            }
        }

        HttpHeaders headers = new HttpHeaders();
        return new ResponseEntity<>("正在使用 nmap 扫描局域网设备，请稍候。。。", headers, HttpStatus.CREATED);
    }

    @ApiOperation("是否扫描完成")
    @GetMapping("isDone")
    public Object isDone() {
        if (future == null) {
            return false;
        }
        try {
            Thread.sleep(1000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        return future.isDone();
    }

    /**
     * 停止扫描
     */
    @ApiOperation("停止扫描")
    @DeleteMapping
    public Object delete() {
        if (future == null) {
            return MessageWrapper.ok("停止成功");
        }
        future.cancel(true);
        future = null;
        startTime = null;

        return MessageWrapper.ok("停止成功");
    }
}
